package top.mh.mybatis.sqlsession;

import top.mh.mybatis.executor.Executor;
import top.mh.mybatis.pojo.Configuration;
import top.mh.mybatis.pojo.MappedStatement;

import java.lang.reflect.*;
import java.util.List;

/**
 * @author mh
 * @version 1.0
 * @description sql会话
 * @since 2024/2/2 16:26
 */
public class DefaultSqlSession implements SqlSession {
    private Configuration configuration;

    private Executor executor;

    public DefaultSqlSession(Configuration configuration, Executor executor) {
        this.configuration = configuration;
        this.executor = executor;
    }

    @Override
    public <T> T selectOne(String statementId, Object param) throws Exception {
        List<T> list = selectList(statementId, param);
        if (list == null || list.isEmpty()) {
            return null;
        } else if (list.size() == 1) {
            return list.get(0);
        } else {
            throw new RuntimeException("查出来的结果集存在多条数据!");
        }
    }


    @Override
    public <T> List<T> selectList(String statementId, Object param) throws Exception {
        MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
        // 将查询操作委派给底层的执行器
        // executor调用底层的JDBC执行查询操作
        List<T> list = executor.query(configuration, mappedStatement, param);
        return list;
    }

    // 生成代理对象
    @Override
    public <T> T getMapper(Class<?> mapperClass) {
        // 基于JDK动态代理产生接口的代理对象
        Object proxy = Proxy.newProxyInstance(DefaultSqlSession.class.getClassLoader(), new Class[]{mapperClass}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                // 提前声明好规范, 每个mapper.xml中的命名空间必须跟接口全路径一致，否则使用代理对象就无法获取命名空间
                String nameSpace = method.getDeclaringClass().getName();
                String statementId = nameSpace + "." + method.getName();
                MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
                switch (mappedStatement.getSqlCommandType()) {
                    case "select":  // 查询操作 问题来了：selectList 还是selectOne?
                        Type genericReturnType = method.getGenericReturnType();
                        // 判断返回类型是否实现泛型类型参数化
                        if (genericReturnType instanceof ParameterizedType) {
                            return selectList(statementId, null);
                        } else {
                            return selectOne(statementId, args[0]);
                        }
                    case "insert":
                        break;
                    case "update":
                        break;
                    case "delete":
                        break;
                }

                return null;
            }
        });
        return (T) proxy;
    }

    @Override
    public void close() {
        executor.close();
    }
}
